import { unserialize } from '@/utils/variable';
import user from '@/store/modules/user';

// namespace.py在层里
const HandlerText =
`
import namespace
import runtimeDeploy

###runtimeDeployHere##

def handler(event, context):
\t__fig__ = {'data': [], 'layout': [] }
`;

function parseLine(line, library, userPrefix) {
  // library只能是pangu或者kaitian
  if (library !== 'pangu' && library !== 'kaitian') {
    return line;
  }
  const index = line.indexOf(`${library}.`);
  if (index === -1) {
    // 没有调用pangu或者kaitian
    return line;
  }

  const leftStr = line.substring(0, index);
  let rest = line.substring(index + library.length + 1); // 获得"library."右边的内容

  const libIndex = rest.indexOf('.');
  if (libIndex === -1) {
    return line;
  }

  const libraryName = rest.substring(0, libIndex); // 获取service名字
  rest = rest.substring(libIndex + 1);

  const funIndex = rest.indexOf('(');
  if (funIndex === -1) {
    return line;
  }

  const functionName = rest.substring(0, funIndex); // 获取function名字
  rest = rest.substring(funIndex);

  // 检查括号是否匹配
  const stack = ['('];
  let i = 1;
  while (!(stack.length === 0) && i < rest.length) {
    if (rest[i] === '(') {
      stack.push('(');
    } else if (rest[i] === ')') {
      stack.pop();
    }
    i++;
  }
  if (stack.length !== 0) {
    // 括号不匹配，停止转义，直接返回
    return line;
  }

  const paramsStr = rest.substring(1, i - 1);
  const rightStr = rest.substring(i);
  let result;
  let params;
  if (!paramsStr) {
    // 参数为空
    params = library === 'pangu' ? `'${libraryName}', '${functionName}'` : `'${libraryName}', '${functionName}', '${userPrefix}'`;
  } else {
    // 先递归转义参数，然后再设置参数
    params = parseLine(paramsStr, library, userPrefix);
    params = library === 'pangu' ? `'${libraryName}', '${functionName}', ${params}` : `'${libraryName}', '${functionName}', '${userPrefix}', ${params}`;
  }
  result = `${leftStr}${library}.invoke(${params})${rightStr}`;

  // 处理调用pangu绘图函数相关的操作
  if (library === 'pangu' && (libraryName === 'plot' || libraryName === 'plot2')) {
    const temp = result.trim();
    if (/^(\w+)(\s*[\\+-\\*/%&\\^\\|]?=\s*).*/g.test(temp)) { // 赋值语句
      const leftVar = temp.replace(/^(\w+)(\s*[\\+-\\*/%&\\^\\|]?=\s*).*/g, '$1');
      if (functionName === 'show') {
        result = `${leftVar} = ${paramsStr}`;
      } else {
        result = result + `\n\t__fig__[\'data\'] += ${leftVar}.get('data', [])\n\t__fig__[\'layout\'] = ${leftVar}.get('layout', {})`;
      }
    } else {
      result = `__fig__[\'data\'] += ${temp}.get('data', [])\n\t__fig__[\'layout\'] = ${temp}.get('layout', {})`;
    }
  }
  return result;
}

// 处理matplotlib原生绘图相关代码;
let pyplotName = 'matplotlib.pyplot';
function parseMatplotlib(line, isHandler) {
  console.log('1' + line);
  const temp = line.replace(/\s+/g, '');
  // 暂时先这么写
  let index = temp.indexOf('importmatplotlib.pyplot');
  if (index === -1) {
    index = temp.indexOf('frommatplotlibimportpyplot');
    // 处理plt.show()函数问题
    if (index === -1) {
      if (temp === pyplotName + '.show()') {
        const leftVar = line.substring(0, line.indexOf(pyplotName));
        return leftVar + 'pangu.show()';
      } else {
        return line;
      }
    } else {
      pyplotName = 'pyplot';
    }
  } else {
    pyplotName = 'matplotlib.pyplot';
  }
  // 获取import模块别名
  const asIndex = temp.indexOf('as');
  if (asIndex !== -1) {
    pyplotName = temp.substring(asIndex + 2);
    pyplotName = pyplotName.trim();
  }
  // 使用matplotlib'Agg'非GUI后端
  const leftVar = line.substring(0, line.indexOf('import'));
  return leftVar + 'import matplotlib;matplotlib.use(\'Agg\');import pangu;' + line.trim();
}

export function translate({ text, userPrefix, isHandler, projectName, needVariable, variableArray }) {
  try {
    const array = text.split('\n');
    const parsedArray = array.map((line) => {
      if (line.trim() === '' || line.trim().charAt(0) === '#') {
        // 是空行或者是注释，原封不动
        return line;
      } else {
        // 不是空行也不是注释，就需要转义
        line = parseLine(line, 'pangu');
        line = parseLine(line, 'kaitian', userPrefix);
        line = parseMatplotlib(line, isHandler);
        // 如果是handler，说明需要缩进
        if (isHandler) {
          line = '\t' + line;
        }
        return line;
      }
    });
    let result = parsedArray.join('\n');
    let insertLines = 0;
    if (isHandler) {
      let variableText = '';
      if (needVariable && variableArray?.length) {
        variableText = unserialize(variableArray);
      }
      let sysPath = '';
      if (projectName) {
        sysPath = `import sys\nsys.path.insert(0, '/code/${projectName}')\n`;
      }
      // 行数
      insertLines = (sysPath + HandlerText + variableText).split('\n').length - 1;
      // 返回的结果需要调用namespace.py里的get_response进行处理
      const newText = HandlerText.replace('###runtimeDeployHere##', `runtimeDeploy.setUserPrefix('${user.state.userprefix}')`);
      result = sysPath + newText + variableText + result + '\n\t' + 'runtimeDeploy.deleteAll()\n\t' +
        'return namespace.get_response(locals())';
      console.log(result);
    }
    return { text: result, insertLines };
  } catch (error) {
    console.error(error);
    return { text };
  }
}

// 逆翻译，把pangu.invoke('service','function', ...)和kaitian.invoke('service', 'function', 'userprefix', ...)翻译成pangu.service.function(...)或者是kaitian.service.function(...)
function rTranslate(line, library) {
  if (library !== 'pangu' && library !== 'kaitian') {
    return line;
  }

  const index = line.indexOf(`${library}.invoke`);
  if (index === -1) {
    return line;
  }
  const leftStr = line.substring(0, index);
  let rest = line.substring(index + library.length + 7); // 获得"library.invoke"右边的内容, 以括号开始

  // 检查括号是否匹配
  const stack = ['('];
  let i = 1;
  while (!(stack.length === 0) && i < rest.length) {
    if (rest[i] === '(') {
      stack.push('(');
    } else if (rest[i] === ')') {
      stack.pop();
    }
    i++;
  }
  if (stack.length !== 0) {
    // 括号不匹配，停止转义，直接返回
    return line;
  }

  const paramsStr = rest.substring(1, i - 1);
  const rightStr = rest.substring(i);

  const regexp = /'/g;

  // array依次保存着单引号位置的信息，前两组单引号里面的内容肯定是serviceName和functionName, 对于kaitian来说第三组单引号内的内容肯定是prefix
  const array = [...paramsStr.matchAll(regexp)];
  const serviceName = paramsStr.substring(array[0]['index'] + 1, array[1]['index']);
  const functionName = paramsStr.substring(array[2]['index'] + 1, array[3]['index']);
  if (library === 'kaitian') {
    // kaitian要忽略第三组单引号
    rest = paramsStr.substring(array[5]['index'] + 3);
  } else {
    rest = paramsStr.substring(array[3]['index'] + 3);
  }
  return `${leftStr}${library}.${serviceName}.${functionName}(${rTranslate(rest, library)})${rightStr}`;
}

export function parseErrorMessage(errorObject, insertLines, isTerminalScript) {
  let errorSource;
  const errorType = errorObject.errorType;
  if (errorType === 'PanguError' || errorType === 'KaitianError') {
    // 调用云函数出错
    errorSource = '云函数内出错!';
    errorObject = JSON.parse(errorObject.errorMessage);
    // if (errorObject.stackTrace && errorType === 'KaitianError') {
    if (errorObject.stackTrace) {
      errorObject.stackTrace.forEach((element, index, array) => {
        if (!(element instanceof Array)) {
          return;
        }
        // 将File "/code/index.py"改成File "/index.py"
        const codeIndex = element[0].indexOf('/code');
        element[0] = element[0].substring(0, codeIndex) + element[0].substring(codeIndex + 5);

        array[index] = element;
      });
    } else {
      // 如果是pangu官方库的云函数出错，隐藏官方库的stackTrace
      delete errorObject.stackTrace;
    }
  } else {
    // 主程序出错

    // 如果是来自命令行的报错，隐藏stackTrace
    if (isTerminalScript) {
      delete errorObject.stackTrace;
      errorSource = '运行命令出错！';
    } else {
      errorSource = '运行主程序出错!';
    }

    // 如果有stackTrace，处理一下stackTrace
    // stackTrace的每一条trace格式例子: ['File "/code/MyFoler/abcd.py"', 'line 17', 'in handler', 'prin(a)']
    const stackTrace = errorObject.stackTrace;
    if (stackTrace) {
      // stackTrace可能是单层数组结构，也可能是双层数组结构，这里是处理双层数组结构的情况
      let oneDimension = false;
      stackTrace.forEach((element, index, array) => {
        // 双层数组结构里每一个元素肯定是Array, 所以碰到不是Array的元素，我们可以判断它必定是单层数组结构
        if (!(element instanceof Array)) {
          oneDimension = true;
          return;
        }
        // 把第一条trace的in handler改成in <module>, 这和python本地程序行为一致
        if (index === 0) {
          element[2] = 'in <module>';
        }

        // 将File "/code/MyFolder/abcd.py"改成File "/MyFolder/abcd.py"
        const codeIndex = element[0].indexOf('/code');
        element[0] = element[0].substring(0, codeIndex) + element[0].substring(codeIndex + 5);

        // 将line 17改成line (17 - insertLines), insertLines是插在用户代码之前的行数
        let number = element[1].substring(5);
        number = parseInt(number) - insertLines;
        element[1] = `line ${number}`;

        // element[3] 可能会出现kaitian.invoke和pangu.invoke，需要逆翻译
        element[3] = rTranslate(element[3], 'kaitian');
        element[3] = rTranslate(element[3], 'pangu');
        array[index] = element;
      });

      // 如果是单层数组结构, 且错误类型为UserCodeSyntaxError, 则需更改stackTrace[0]和stackTrace[1]
      if (oneDimension) {
        if (errorObject.errorType === 'UserCodeSyntaxError') {
          const codeIndex = stackTrace[0].indexOf('/code');
          stackTrace[0] = stackTrace[0].substring(0, codeIndex) + stackTrace[0].substring(codeIndex + 5);

          let number = stackTrace[1].substring(5);
          number = parseInt(number) - insertLines;
          stackTrace[1] = `line ${number}`;
        }
      }
    }
  }
  const userPrefix = user.state.userprefix;
  const errorLog = JSON.stringify(errorObject, null, '\t').replaceAll(userPrefix, '');

  return { errorSource, errorLog };
}

